/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package common.xmlObjects;

import java.util.Vector;

/**
 * The XMLNode class allows for the creation of XML nodes. This class is built
 * off my interpretation of XML.
 * <code>
 * < root >
 *   < name attribute="value"> data </ name >
 *  < child >
 *
 * </ child >
 * </code>
 * @author Richard Irons
 */
public class XMLNode {
    private String nodeName;
    private String nodeData;
    private Vector<XMLAttribute> attributes;
    private Vector<XMLNode> childNodes;
    private String nodeType;

    public static final String NDEF = "not defined";
    public static final String CHILD = "child";
    public static final String ROOT = "root";

    public String getNodeType() {
        return nodeType;
    }

    public void setNodeType(String nodeType) {
        this.nodeType = nodeType;
    }

    public XMLNode() {
        nodeName = "";
        attributes = null;
        childNodes = null;
        nodeData = "";
        nodeType = NDEF;
    }
    public XMLNode(String nodeName, String nodeData) {
        this.nodeName = nodeName;
        this.nodeData = nodeData;
        attributes = null;
        childNodes = null;
        nodeType = NDEF;
    }
    public String toXMLString() {
        // the final xml string will be stored here
        String xml = "";
        //any atributes
        String attrib = "";

        //find any attributes from current node and add thier toString value to
        //the attribs variable.
        if (attributes != null) {
            for (XMLAttribute a : attributes) {
                attrib += a.toString();
            }
        }
        //apply tabs infront of the open tag. Tag contains the nodeName and attributes
        xml += "<" + nodeName + attrib +  ">";
        //add any nodeData between the tags, check first that its not null. This
        //wont cause an exception, it will print null if the check isnt made however
        if (nodeData==null || nodeData.isEmpty() == true) {
        }
        else {
            xml+=nodeData;
        }
        //get all of the children of this node and add thier toString to this
        //this is the recursion, the children are of the same type so this
        //toXMLString function will call itself but in a different instance
        if (childNodes != null && childNodes.size() > 0) {
            for (XMLNode c : childNodes) {
                xml += c.toXMLString();
            }
        }
        //closing tag
        xml +=  "</" + nodeName + ">";
        //return the final xml code.
        return xml;
    }
    /**
     * Overide the toString function of this class. The new toString calls a
     * second toString function that takes 2 parameters, depth and prevDepth.
     * The 2 parameters are for formating purposes only. At the first level
     * the same number should be sent at the same time.
     * @return The xml of this node down as a string
     */
    //NOTE!!!! MUST PASS 0,0 OR IT WILL FAIL!!!
    @Override
    public String toString() {
        return toString(0,0);
    }
    /**
     * This is the code to format the xml code. It is a recursive function as this
     * will be called for every node connected.
     * @param depth - current depth
     * @param prevDepth - previous depth, will be 1 less then current depth until
     * the recursive loops back
     * @return - A nicely fomated xml from the XMLNodes that are connected
     */
    private String toString(int depth, int prevDepth) {
        // the final xml string will be stored here
        String xml = "";
        //any atributes
        String attrib = "";
        //variable to store tabs, the number of tabs is equivalent to
        //the current depth. eq depth 0 = no tabs, depth 1 = 1 tab etc.
        String tabs = "";
        //a tab, \\t is to much
        String tab = "    ";

        //find any attributes from current node and add thier toString value to
        //the attribs variable.
        if (attributes != null) {
            for (XMLAttribute a : attributes) {
                attrib += a.toString();
            }
        }
        //get the number of tabs
        for (int i = 0; i < depth; i++) {
            tabs += tab;
        }
        //apply tabs infront of the open tag. Tag contains the nodeName and attributes
        xml += tabs + "<" + nodeName + attrib +  ">";
        //add any nodeData between the tags, check first that its not null. This
        //wont cause an exception, it will print null if the check isnt made however
        if (nodeData==null || nodeData.isEmpty() == true) {
        }
        else {
            xml+=nodeData;
        }
        //get all of the children of this node and add thier toString to this
        //this is the recursion, the children are of the same type so this
        //toString function will call itself but in a different instance
        if (childNodes != null && childNodes.size() > 0) {
            for (XMLNode c : childNodes) {
                xml += "\n" + c.toString(depth+1, depth);
            }
            //i increase the previous depth here because the previous depth
            //is a variable that when it equals the same as depth. Hard to explain
            //why i in crease it but best way is, since i have moved a depth down
            //and then come back i need a new line and the only way to get a new
            //line is when prev = depth, so i increase it which means that they
            //will be equal -- the following is a print out example with this
            //prevDepth++ running. d=depth, p=prevDepth
            //where the 2 number match is where a new line is required
            //<component> d:0 p:0
            //        <id> d:1 p:0
            //                <name>sup</name> d:2 p:1
            //                <description> d:2 p:1
            //                        <stock>ww</stock> d:3 p:2
            //                        <stock>ww</stock> d:3 p:2
            //                </description> d:2 p:2
            //        </id> d:1 p:1
            //</component> d:0 p:1
            prevDepth++;
        }
        //if the depth and prevDepth are the same then your back at the previous
        //depth and require a newline and also the current tabs for that level\
        //this also check against depth == 0 because the previous depth starts
        //at 0 as well as depth and is set to 1 if there are children inside of
        //the root node. therefore the prev depth and wouldnt be the same
        if(depth == prevDepth || (depth == 0 && depth != prevDepth)) {
            xml += "\n" + tabs;
        }
        //closing tag
        xml +=  "</" + nodeName + ">";
        //return the final xml code.
        return xml;
    }

    public void addChildNode(XMLNode child) {
        if (child != null) {
            if (this.childNodes == null ) {
                childNodes = new Vector<XMLNode>();
            }
            if (nodeData == null || nodeData.isEmpty()) {
                if (child.getNodeType().equals(CHILD) == false) {
                    child.setNodeType(CHILD);
                }
                this.childNodes.add(child);
            }
            else {
                System.out.println("Cannont add childNode to:\'"+nodeName+
                        "\' as it has data, set nodeData to null or \"\"");
            }
        }
    }
    public void removeChildNode(XMLNode child){
        if (this.childNodes != null) {
            this.childNodes.remove(child);

            if(this.childNodes.isEmpty()) {
                this.childNodes = null;
            }
        }        
    }
    public void removeAllChildNodes() {
        if (this.childNodes != null) {
            this.childNodes.removeAllElements();
        }
        this.childNodes = null;
    }
    public void addAtrribute(XMLAttribute attribute) {
        if (this.attributes == null) {
            attributes = new Vector<XMLAttribute>();
        }
        this.attributes.add(attribute);
    }
    public void removeAtrribute(XMLAttribute attribute){
        if (this.attributes != null) {
            this.attributes.remove(attribute);
            
            if(this.attributes.isEmpty()) {
                this.attributes = null;
            }
        }
    }
    public void removeAllAtrributes() {
        if (this.attributes != null) {
            this.attributes.removeAllElements();
        }
        this.attributes = null;
    }
    public Vector<XMLAttribute> getAttributes() {
        return attributes;
    }

    public void setAttributes(Vector<XMLAttribute> attributes) {
        this.attributes = attributes;
    }

    public Vector<XMLNode> getChildNodes() {
        return childNodes;
    }

    public void setChildNodes(Vector<XMLNode> childNodes) {
        this.childNodes = childNodes;
    }

    public String getNodeName() {
        return nodeName;
    }

    public void setNodeName(String nodeName) {
        this.nodeName = nodeName;
    }

    public String getNodeData() {
        return nodeData;
    }

    public void setNodeData(String nodeData) {
        this.nodeData = nodeData;
    }
}
